
package com.xing.lei.happy.thrid.flh;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.*;
import java.io.StringReader;
import org.xml.sax.InputSource;
import java.util.*;

public class XmlToMap {

    /**
     * 将XML字符串转换为Map，允许指定目标标签名称。
     * @param xml XML字符串
     * @param tagName 目标标签名称
     * @return 包含目标标签内数据的Map
     */
    public static Map<String, Object> xmlStringToMap(String xml, String tagName) {
        validateInput(xml, tagName);
        String rootContent = extractContentWithinTags(xml, tagName);
        Document document = parseXmlSnippet(rootContent);
        Element rootElement = getRootElement(document, tagName);
        Map<String, Object> mapWithTagName = parseElement(rootElement);
        if(mapWithTagName.get(tagName) != null && mapWithTagName.get(tagName) instanceof Map){
            return (Map)mapWithTagName.get(tagName);
        }
        return mapWithTagName;
    }

    private static void validateInput(String xml, String tagName) {
        if (xml == null || xml.isEmpty() || tagName == null || tagName.isEmpty()) {
            throw new IllegalArgumentException("XML string and tag name must not be null or empty.");
        }
    }

    private static String extractContentWithinTags(String xml, String tagName) {
        int start = xml.indexOf("<" + tagName + ">") != -1 ? xml.indexOf("<" + tagName + ">") : xml.indexOf("<" + tagName + " ");
        int end = xml.indexOf("</" + tagName + ">");
        if (start == -1 || end == -1) {
            throw new IllegalArgumentException("Tag '" + tagName + "' not found in the XML string.");
        }
        return xml.substring(start, end + ("<" + tagName + ">").length() + 1);
    }

    private static Document parseXmlSnippet(String rootContent) {
        try {
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = factory.newDocumentBuilder();
            return builder.parse(new InputSource(new StringReader(rootContent)));
        } catch (Exception e) {
            throw new RuntimeException("Error parsing XML snippet into a DOM Document.", e);
        }
    }

    private static Element getRootElement(Document document, String tagName) {
        NodeList nodeList = document.getElementsByTagName(tagName);
        if (nodeList.getLength() == 0) {
            throw new IllegalArgumentException("Tag '" + tagName + "' not found in the XML string.");
        }
        return (Element) nodeList.item(0);
    }

    private static Map<String, Object> parseElement(Element element) {
        Map<String, Object> map = new LinkedHashMap<>();
        List<Object> values = new ArrayList<>();

        NodeList nodeList = element.getChildNodes();
        boolean hasTextOrAttribute = false;

        for (int i = 0; i < nodeList.getLength(); i++) {
            Node node = nodeList.item(i);
            if (node instanceof Element) {
                Element childElement = (Element) node;
                Object value = parseElement(childElement);
                values.add(value);
            } else if (node.getNodeType() == Node.TEXT_NODE && !node.getTextContent().trim().isEmpty()) {
                hasTextOrAttribute = true;
                values.add(node.getTextContent().trim());
            }
        }

        NamedNodeMap attributes = element.getAttributes();
        for (int i = 0; i < attributes.getLength(); i++) {
            Attr attribute = (Attr) attributes.item(i);
            hasTextOrAttribute = true;
            values.add(attribute.getValue());
        }

        // 如果只有一个子元素或值，并且没有其他属性或文本节点，直接添加其内容
        if (values.size() == 1 && !hasTextOrAttribute) {
            return (Map<String, Object>) values.get(0);
        }

        // 否则，将元素及其所有子元素作为一个整体的 Map 添加
        String tagName = element.getTagName();
        if (values.isEmpty()) {
            map.put(tagName, null);
        } else if (values.size() == 1) {
            map.put(tagName, values.get(0));
        } else {
            Map<String, Object> subMap = new LinkedHashMap<>();
            for (Object value : values) {
                if (value instanceof Map) {
                    subMap.putAll((Map<String, Object>) value);
                } else {
                    subMap.put(tagName, value);
                }
            }
            map.put(tagName, subMap);
        }

        return map;
    }
}